package org.hzero.interfaces.common.handler;

import org.hzero.interfaces.common.constants.KnifeInterfaceCodeEnum;
import org.hzero.interfaces.common.constants.KnifeInterfaceLogDirection;
import org.hzero.interfaces.common.entity.KnifeInterfaceLogRecord;
import org.hzero.interfaces.common.helper.director.KnifeInterfaceLogDirector;
import org.hzero.interfaces.common.helper.impl.KnifeRestTemplateInterfaceLogHelperImpl;
import org.hzero.interfaces.common.utils.KnifeLogParamUtil;
import org.hzero.interfaces.common.utils.KnifeReflectUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.BindingResult;
import org.springframework.web.client.RestOperations;

import javax.servlet.ServletRequest;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Date;
import java.util.HashSet;
import java.util.Set;

/**
 * #123:添加接口调用监控日志
 * jdk动态代理
 * 1.日志记录采用异步操作2.记录日志的方法独立,能够支持多线程3.使用线程池来记录调用日志记录
 *
 * @author: tianhao.luo@hand-china.com 2021/6/30  上午9:03
 */
public class KnifeJdkHandler implements InvocationHandler {

    private static final Logger LOGGER = LoggerFactory.getLogger(KnifeJdkHandler.class);

    /**
     * 代理对象
     */
    private final RestOperations restTemplate;
    /**
     * 如果不传入methodNames,则不会进行监控
     */
    private Set<String> methodNameSet;

    /**
     * 接口编码及描述
     */
    private final KnifeInterfaceCodeEnum knifeInterfaceCodeEnum;

    public KnifeJdkHandler(RestOperations restTemplate, KnifeInterfaceCodeEnum knifeInterfaceCodeEnum, String... methodNames) {
        this.restTemplate = restTemplate;
        this.knifeInterfaceCodeEnum = knifeInterfaceCodeEnum;
        if (methodNames != null && methodNames.length != 0) {
            methodNameSet = new HashSet<>(methodNames.length);
            methodNameSet.addAll(Arrays.asList(methodNames));
        }
    }


    @Override
    public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
        String name = method.getName();
        if (methodNameSet != null && methodNameSet.contains(name)) {
            Object[] objects = KnifeLogParamUtil.deepCloneArgs(args, ServletRequest.class, BindingResult.class);
            Date startDate = new Date();
            Object invoke = null;
            KnifeRestTemplateInterfaceLogHelperImpl defaultHitfInterfaceLogHelper = null;
            KnifeInterfaceLogRecord knifeInterfaceLogRecord = new KnifeInterfaceLogRecord(startDate, KnifeInterfaceLogDirection.OUT, knifeInterfaceCodeEnum);
            try {
                invoke = method.invoke(restTemplate, args);
                defaultHitfInterfaceLogHelper = new KnifeRestTemplateInterfaceLogHelperImpl(knifeInterfaceLogRecord);
                KnifeInterfaceLogDirector knifeInterfaceLogDirector = new KnifeInterfaceLogDirector(defaultHitfInterfaceLogHelper);
                knifeInterfaceLogDirector.createHitfInterfaceLog(objects, (ResponseEntity<?>) invoke, method);
            } catch (Exception e) {
                String errorMsg = "";
                if (e instanceof InvocationTargetException){
                    InvocationTargetException invocationTargetException = (InvocationTargetException) e;
                    Throwable targetException = invocationTargetException.getTargetException();
                    Class<? extends Throwable> aClass = targetException.getClass();
                    Method getResponseBodyAsString = KnifeReflectUtil.getMethodNotRecursion(aClass, "getResponseBodyAsString");
                    if(getResponseBodyAsString != null){
                        if (!getResponseBodyAsString.isAccessible()) getResponseBodyAsString.setAccessible(true);
                        errorMsg = (String) getResponseBodyAsString.invoke(targetException);
                        e = new Exception(errorMsg);
                    }
                }
                defaultHitfInterfaceLogHelper = new KnifeRestTemplateInterfaceLogHelperImpl(knifeInterfaceLogRecord, e);
                KnifeInterfaceLogDirector knifeInterfaceLogDirector = new KnifeInterfaceLogDirector(defaultHitfInterfaceLogHelper);
                knifeInterfaceLogDirector.createHitfInterfaceLog(objects, (ResponseEntity<?>) invoke, method);
            }
            try {

            } catch (Exception e) {
                LOGGER.error("jdk代理记录日志失败：{}",e.getMessage());
            }
            return invoke;
        } else {
            return method.invoke(restTemplate, args);
        }
    }
}
